/*****************************************************************************
* @brief   nand hal fmc
*****************************************************************************/
#include "bsp.h"
#include "bsp_atk_nand.h"
#include "nand_lut.h"
#include "mem_alloc.h"

/*****************************************************************************
* @brief   INFO
*****************************************************************************/
uu8 *nand_cache;

uint8_t fmc_nand_ecc_error_get(void);
uint8_t fmc_nand_read_page(uint32_t page_addr);
uint8_t fmc_nand_read_cache(uint16_t cell, uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_read_spare(uint32_t page_addr, uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_write_cache(uint16_t cell, const uint8_t *dat, uint16_t Len);
uint8_t fmc_nand_program_exe(uint32_t page_addr);
uint8_t fmc_nand_block_erase(uint32_t block_addr);

/*****************************************************************************
* @brief   hal interface link
*****************************************************************************/
char nand_fmc_dev_link(nand_lut_type *lut)
{
	dword_tt id;
	char cnt;

	if (nand_cache == 0)
	{
		nand_cache = mem_ccm.api->malloc(4096 + 16);
	}

	// spi nand hal link
	lut->hal.ecc_error_get = fmc_nand_ecc_error_get;
	lut->hal.read_page = fmc_nand_read_page;
	lut->hal.read_cache = fmc_nand_read_cache;
	lut->hal.read_spare = fmc_nand_read_spare;
	lut->hal.write_cache = fmc_nand_write_cache;
	lut->hal.program_exe = fmc_nand_program_exe;
	lut->hal.init = 0;
	lut->hal.block_erase = fmc_nand_block_erase;

	// chip infomation recognize
	for (cnt = 0; cnt < 5; cnt++)
	{
		// read ID
		id.uVal = atk_nand_read_id();

		// w25n 1Gbits
		if (id.uVal == MT29F1G08ABADA)
		{
			lut->page_size = 2048 + 16;
			lut->page_main_size = 2048;
			lut->page_spare_size = 16;
			lut->page_per_block = 64;
			lut->block_per_plane = 1024;
			lut->block_total = 1024;
			lut->logic_block_amount = lut->block_total * 7 / 8;
			lut->keep_err_logic = 0;

			break;
		}
		else
		{
			lut->page_size = 2048 + 16;
			lut->page_main_size = 2048;
			lut->page_spare_size = 16;
			lut->page_per_block = 64;
			lut->block_per_plane = 1024;
			lut->block_total = 1024;
			lut->logic_block_amount = lut->block_total * 7 / 8;
			lut->keep_err_logic = 0;
		}
	}

	return 0;
}

/*****************************************************************************
* @brief   get ecc
*****************************************************************************/
uint8_t fmc_nand_ecc_error_get(void) //1-ecc error, 0-ecc not error
{
	return atk_nand.ecc_err_st;
}

/*****************************************************************************
* @brief   read page
*****************************************************************************/
uint8_t fmc_nand_read_page(uint32_t page_addr)
{
	int cnt = 3;

	while (cnt--)
	{
		atk_nand_read_cpage(page_addr, nand_cache);
		if (0 == atk_nand.ecc_err_st)
		{
			break;
		}
	}

	/*---spare 区独立处理，此处不再检测
	if(0 != comp_u32(&nand_cache[2048], &nand_cache[2048+8], 2))
	{
		atk_nand.ecc_err_st++;
	} */

	if (cnt < 0)
	{
		lut_print("nand ecc fail @ page: %d\n", page_addr);
	}

	return 0;
}

/*****************************************************************************
* @brief   read cache
*****************************************************************************/
uint8_t fmc_nand_read_cache(uint16_t cell, uint8_t *dat, uint16_t Len)
{
	copy_u8(&nand_cache[cell], dat, Len);

	return 0;
}

/*****************************************************************************
* @brief   write cache
*****************************************************************************/
uint8_t fmc_nand_write_cache(uint16_t cell, const uint8_t *dat, uint16_t Len)
{
	copy_u8(dat, &nand_cache[cell], Len);

	return 0;
}

/*****************************************************************************
* @brief   read spare
*****************************************************************************/
uint8_t fmc_nand_read_spare(uint32_t page_addr, uint8_t *dat, uint16_t Len)
{
	int cnt = 3;

	while (cnt--)
	{
		atk_nand_read_spare(page_addr, 0, nand_cache, 16);
		if (comp_u8(&nand_cache[0], &nand_cache[8], 8) == 0)
			break;
	}

	copy_u8(nand_cache, dat, Len);

	if (cnt < 0)
	{
		atk_nand.ecc_err_st++;
		lut_print("nand spare not compare @ page: %d\n", page_addr);
	}

	return 0;
}

/*****************************************************************************
* @brief   program
*****************************************************************************/
uint8_t fmc_nand_program_exe(uint32_t page_addr)
{
	copy_u32(&nand_cache[2048], &nand_cache[2048 + 8], 2);
	atk_nand_write_cpage(page_addr, nand_cache);

	return 0;
}

/*****************************************************************************
* @brief   erase
*****************************************************************************/
uint8_t fmc_nand_block_erase(uint32_t block_addr)
{
	atk_nand_erase_block(block_addr);

	return 0;
}
